/* 
 * Copyright (C) 2012 UTSChedule
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package utsc.apps.utschedule.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import android.text.TextUtils;

/**
 * A helper class to manage date time related operations.
 * @author Wenbin
 *
 */
public class TimetableHelper {
	
	public static final int TYPE_TIME=0;
	public static final int TYPE_DATE=1;
	public static final int TYPE_DATETIME=2;
	public static final int TYPE_MONTH=3;
	public static final int TYPE_WEEKDAY=4;
	public static final int TYPE_MONTH_DATE=5;
	public static final int TYPE_DATE_WEEKDAY=6;
	public static final int TYPE_TIME24=7;
	public static final int TYPE_DATETIME_EXAM=8;
	private static GregorianCalendar CALENDAR ;
	private static final String TIME_FORMAT="h:mm a";
	private static final String DATE_FORMAT="yyyy-MM-dd";
	private static final String DATETIME_FORMAT=DATE_FORMAT+" "+TIME_FORMAT;
	private static final String MONTH_FORMAT="MMM";
	private static final String WEEKDAY_FORMAT="EEEE";
	private static final String MONTH_DATE_FORMAT=MONTH_FORMAT+" dd";
	private static final String DATE_WEEKDAY_FORMAT=WEEKDAY_FORMAT+", "+DATE_FORMAT;
	private static final String TIME24_FORMAT="k:mm";
	private static final String DATETIME_EXAM_FORMAT="dd/MM/yyyy H:mm";
	
	private static final SimpleDateFormat DATETIME_FORMATER=new SimpleDateFormat(TIME_FORMAT);
	public static final int SESSION_WINTER=0;
	public static final int SESSION_FALL=2;
	public static final int SESSION_SUMMER=1;

	/**
	 * Return the current term (YEAR + START_MONTH_OF_THE_TERM)
	 * @return
	 */
	public static String getCurrentTermString(){
		setToNow();
		int year=CALENDAR.get(Calendar.YEAR);
		int session=getSession();
		String term=null;
		switch (session) {
		case TimetableHelper.SESSION_WINTER:
			term=year+"1";
			break;
		case TimetableHelper.SESSION_SUMMER:
			term=year+"5";
			break;
		case TimetableHelper.SESSION_FALL:
			term=year+"9";
			break;
		default:
			break;
		}
		return term;
	}
	
	/**
	 * Return the current week days (each weekday is the day from month) 
	 * @param week
	 * @param days
	 * @param isSundayFirst
	 * @return
	 */
	public static int[] getWeekDays(int week, int days, boolean isSundayFirst) {
		//getCurrentWeekNumber();
		setToNow();
		int[] results = new int[days];
		CALENDAR.set(Calendar.WEEK_OF_YEAR, week);
		CALENDAR.get(Calendar.WEEK_OF_YEAR);
		int startDay = Calendar.MONDAY;
		int startOffset = 2;
		if (isSundayFirst) {
			startDay = Calendar.SUNDAY;
			startOffset = 1;
		}
		CALENDAR.set(Calendar.DAY_OF_WEEK, startDay);
		//int startWeekDay=CALENDAR.get(Calendar.DAY_OF_MONTH);
		
		int lastDay = days + startDay - 1;
		for (int i = startDay; i <= lastDay; i++) {
			CALENDAR.set(Calendar.DAY_OF_WEEK, i);
			results[i - startOffset] = CALENDAR.get(Calendar.DAY_OF_MONTH);
		}
		return results;

	}
	
	/**
	 * Get the day in a String format by using the {@code TYPE_MONTH_DATE} format.
	 * @param dayOfYear
	 * @return
	 */
	public static String getDayString(int dayOfYear){
		setToNow();
		CALENDAR.set(Calendar.DAY_OF_YEAR, dayOfYear);
		
		return getFormattedString(CALENDAR.getTimeInMillis(), TYPE_MONTH_DATE);
	}
	
	/**
	 * Return the month by the given {@link Calendar} value and field.
	 * @param value
	 * @param calendarSetField
	 * @return
	 */
	public static int getMonth(int value, int calendarSetField){
			setToNow();
			CALENDAR.set(calendarSetField, value);
		return CALENDAR.get(Calendar.MONTH);
	}
	
	/**
	 * Return the day of month by the given {@link Calendar} value and field.
	 * @param value
	 * @param calendarSetField
	 * @return
	 */
	public static int getDay(int value, int calendarSetField){
		setToNow();
		CALENDAR.set(calendarSetField, value);
		return CALENDAR.get(Calendar.DAY_OF_MONTH);
	}
	
	/**
	 * Return the day of year by the given {@link Calendar} value and field.
	 * @param value
	 * @param calendarSetField
	 * @return
	 */
	public static int getDayOfYear(int value, int calendarSetField){
		setToNow();
		CALENDAR.set(calendarSetField, value);
		return CALENDAR.get(Calendar.DAY_OF_YEAR);
	}
	
	/**
	 * Return the month by the 2 given {@link Calendar} values and fields.
	 * @param value
	 * @param calendarSetField
	 * @param value2
	 * @param calendarSetField2
	 * @return
	 */
	public static int getDayOfYear(int value, int calendarSetField,int value2,int calendarSetField2){
		setToNow();
		CALENDAR.set(calendarSetField, value);
		CALENDAR.get(calendarSetField);
		CALENDAR.set(calendarSetField2, value2);
		return CALENDAR.get(Calendar.DAY_OF_YEAR);
	}
	
	/**
	 * Return the week of year by the given {@link Calendar} value and field.
	 * @param value
	 * @param calendarSetField
	 * @return
	 */
	public static int getWeekOfYear(int value, int calendarSetField){
		setToNow();
		CALENDAR.set(calendarSetField, value);
		return CALENDAR.get(Calendar.WEEK_OF_YEAR);
	}
	
	/**
	 * Return the start week of year by the given {@link Calendar} month.
	 * @param calendarMonth
	 * @return
	 */
	public static int getStartWeekOfYearByMonth(int calendarMonth){
		setToNow();
		CALENDAR.set(CALENDAR.get(Calendar.YEAR), calendarMonth, 1);
		return CALENDAR.get(Calendar.WEEK_OF_YEAR);
	}
	
	/**
	 * Return the week day by the given {@link Calendar} value and field.
	 * @param value
	 * @param calendarSetField
	 * @return
	 */
	public static int getWeekDay(int value, int calendarSetField){
		setToNow();
		CALENDAR.set(calendarSetField, value);
		return CALENDAR.get(Calendar.DAY_OF_WEEK)-1;
	}
	
	/**
	 * Return the current day of year.
	 * @return
	 */
	public static int getDayOfYear(){
		setToNow();
		return CALENDAR.get(Calendar.DAY_OF_YEAR);
	}
	
	/**
	 * Return the current week of year.
	 * @param millis
	 * @return
	 */
	public static int getWeekOfYear(long millis){
		setToNow();
		CALENDAR.setTimeInMillis(millis);
		return CALENDAR.get(Calendar.WEEK_OF_YEAR);
	}
	
	/**
	 * Return the week day range string by the given week of year.
	 * @param weekOfYear
	 * @return
	 */
	public static String getWeekRangeString(int weekOfYear){
		//CALENDAR.clear();
		setToNow();
		CALENDAR.set(Calendar.WEEK_OF_YEAR, weekOfYear);
		CALENDAR.get(Calendar.WEEK_OF_YEAR);
		String month1String=" ";
		//int month=CALENDAR.get(Calendar.MONTH);
		String month2String=" - ";
		CALENDAR.set(Calendar.DAY_OF_WEEK,Calendar.MONDAY);
		int monday=CALENDAR.get(Calendar.DAY_OF_MONTH);
		CALENDAR.set(Calendar.DAY_OF_WEEK,Calendar.FRIDAY);
		int friday=CALENDAR.get(Calendar.DAY_OF_MONTH);
		
		if (monday>friday) {
			month2String+=getFormattedString(CALENDAR.getTimeInMillis(),TYPE_MONTH)+" ";//CALENDAR.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.CANADA)+" ";
			CALENDAR.set(Calendar.WEEK_OF_YEAR, weekOfYear-1);
			CALENDAR.get(Calendar.WEEK_OF_YEAR);
		}
			month1String=getFormattedString(CALENDAR.getTimeInMillis(),TYPE_MONTH)+month1String;//CALENDAR.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.CANADA)+month1String;
		
		return month1String+monday+month2String+friday; 
	}

	/*public static long getPredefinedTime(int month,int day){
		setToNow();
		CALENDAR.set(Calendar.MONTH, month);
		CALENDAR.set(Calendar.DAY_OF_MONTH, day);
		return CALENDAR.getTimeInMillis();
	}*/
	
	/**
	 * Return a timestamp from the given week of year
	 * @param weekOfYear
	 * @return
	 */
	public static long getPredefinedTime(int weekOfYear){
		setToNow();
		CALENDAR.set(Calendar.WEEK_OF_YEAR,weekOfYear);
		return CALENDAR.getTimeInMillis();
	}
	
	/**
	 * Return a timestamp by the given {@link Calendar} value and field.
	 * @param value
	 * @param fieldSet
	 * @return
	 */
	public static long getPredefinedTime(int value, int fieldSet){
		setToNow();
		CALENDAR.set(fieldSet,value);
		return CALENDAR.getTimeInMillis();
	}
	
	/*public static int[] getWeekDays(int week, int days){
		int[] results = new int[days];
		TIME.setToNow();
		TIME.
	}*/
	
	/**
	 * Return the current session term.
	 * @return
	 */
	public static int getSession(){
		setToNow();
		CALENDAR.setTime(new Date(System.currentTimeMillis()));
		int month=CALENDAR.get(Calendar.MONTH);
		if (month>=Calendar.JANUARY&&month<=Calendar.APRIL) {
			return SESSION_WINTER;
		}else if (month>=Calendar.MAY && month <=Calendar.AUGUST) {
			return SESSION_SUMMER;
		}else{
			return SESSION_FALL;
		}
	}

	/**
	 * Return the session term by the given month value.
	 * @param month
	 * @return
	 */
	public static int getSession(int month){
		
		if (month>=Calendar.JANUARY&&month<=Calendar.APRIL) {
			return SESSION_WINTER;
		}else if (month>=Calendar.MAY && month <=Calendar.AUGUST) {
			return SESSION_SUMMER;
		}else{
			return SESSION_FALL;
		}
	}
	
	/**
	 * Return the current week number.
	 * @return
	 */
	public static int getCurrentWeekNumber(){
		setToNow();
		return CALENDAR.get(Calendar.WEEK_OF_YEAR);
	}
	
	/**
	 * Reset the underlying {@link Calendar} to the current time.
	 */
	public static void setToNow(){
		if (CALENDAR==null) {
			CALENDAR=(GregorianCalendar) GregorianCalendar.getInstance(Locale.getDefault());
		}
		//CALENDAR.setTime(new Date(System.currentTimeMillis()));
		CALENDAR.setTimeInMillis(System.currentTimeMillis());
		CALENDAR.setFirstDayOfWeek(Calendar.MONDAY);
		CALENDAR.get(Calendar.YEAR);
	}
	
	public static String getHourString(int hour,int minutes){
		String txtMin=""+minutes;
		if (minutes==0) {
			txtMin="00";
		}else if(minutes<10) {
			txtMin="0"+minutes;
		}
		if (hour>12) {
			if (hour==24) {
				return "00:"+txtMin+" AM";
			}
			return hour-12+":"+txtMin+" PM";
		}else {
			if (hour<10) {
				return "0"+hour+":"+txtMin+" AM";
			}else if (hour==12) {
				return "12:"+txtMin+" PM";
			}
			return hour+":"+txtMin+" AM";
		}
	}
	
	public static String getTimeString (long time){
		DATETIME_FORMATER.applyPattern(TIME_FORMAT);
		return DATETIME_FORMATER.format(new Date(time));
	}
	
	public static String getFormattedString(long time, int type){
		String ret=null;
		if (time!=0) {
			Date date=new Date(time);
			//CALENDAR.setTime(date);
			switch (type) {
			case TYPE_DATE:
				DATETIME_FORMATER.applyPattern(DATE_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_DATETIME:
				DATETIME_FORMATER.applyPattern(DATETIME_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_MONTH:
				DATETIME_FORMATER.applyPattern(MONTH_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_TIME:
				DATETIME_FORMATER.applyPattern(TIME_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_WEEKDAY:
				DATETIME_FORMATER.applyPattern(WEEKDAY_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_MONTH_DATE:
				DATETIME_FORMATER.applyPattern(MONTH_DATE_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_DATE_WEEKDAY:
				DATETIME_FORMATER.applyPattern(DATE_WEEKDAY_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_TIME24:
				DATETIME_FORMATER.applyPattern(TIME24_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			case TYPE_DATETIME_EXAM:
				DATETIME_FORMATER.applyPattern(DATETIME_EXAM_FORMAT);
				ret=DATETIME_FORMATER.format(date);
				break;
			default:
				break;
			}
		}
		return ret;
	}
	
	public static Calendar convertFromDateTimeString(String dateTimeString, int type) {
		Date d=convertFromDateTimeStringToMillis(dateTimeString, type);
		if (d != null) {
			Calendar c=Calendar.getInstance();
			c.setTime(d);
			return c;
		}
		return null;
	}
	
	public static Date convertFromDateTimeStringToMillis(String dateTimeString, int type) {
		try {
			if (dateTimeString != null) {
				switch (type) {
					case TYPE_TIME:
						DATETIME_FORMATER.applyPattern(TIME_FORMAT);
						break;
					case TYPE_DATE:
						DATETIME_FORMATER.applyPattern(DATE_FORMAT);
						break;
					case TYPE_DATETIME:
						DATETIME_FORMATER.applyPattern(DATETIME_FORMAT);
						break;
					case TYPE_MONTH:
						DATETIME_FORMATER.applyPattern(MONTH_FORMAT);
						break;
					case TYPE_MONTH_DATE:
						DATETIME_FORMATER.applyPattern(MONTH_DATE_FORMAT);
						break;
					case TYPE_WEEKDAY:
						DATETIME_FORMATER.applyPattern(WEEKDAY_FORMAT);
						break;
					case TYPE_DATE_WEEKDAY:
						DATETIME_FORMATER.applyPattern(DATE_WEEKDAY_FORMAT);
						break;
					case TYPE_TIME24:
						DATETIME_FORMATER.applyPattern(TIME24_FORMAT);
						break;
					case TYPE_DATETIME_EXAM:
						DATETIME_FORMATER.applyPattern(DATETIME_EXAM_FORMAT);
						break;
					default:
						break;
				}
				Date d=DATETIME_FORMATER.parse(dateTimeString);
				return d;
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static int parseSearchDate(String weekdayString) {
		if (!TextUtils.isEmpty(weekdayString)) {
			if (weekdayString.equals("MO")) {
				return 1;
			}else if (weekdayString.equals("TU")) {
				return 2;
			}else if (weekdayString.equals("WE")) {
				return 3;
			}else if (weekdayString.equals("TH")) {
				return 4;
			}else if (weekdayString.equals("FR")) {
				return 5;
			}else {
				return 1;
			}
		}
		return 1;
	}
	
	public static Calendar getCurrentDateCalendar(){
		setToNow();
		//CALENDAR.setTime(new Date(System.currentTimeMillis()));
		return CALENDAR;
	}
	
	public static long getTimeFromString(String timeString){
		DATETIME_FORMATER.applyPattern(TIME_FORMAT);
		try {
			long ret= DATETIME_FORMATER.parse(timeString).getTime();
			return ret;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	/**
	 * Get today as in week day number.
	 * @return
	 */
	public static int getTodayWeekday(){
		setToNow();
		//CALENDAR.setTime(new Date(System.currentTimeMillis()));
		return CALENDAR.get(Calendar.DAY_OF_WEEK);
	}
	
	public static String getWeekdayString(int calendarInt){
		setToNow();
		CALENDAR.set(Calendar.DAY_OF_WEEK, calendarInt);
		return getFormattedString(CALENDAR.getTimeInMillis(), TYPE_WEEKDAY);//.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.CANADA);
	}

}
